#include "apue.h"
#include <errno.h>
#include <limits.h>
#include <fcntl.h>

#ifdef PATH_MAX
static long pathmax= PATH_MAX;
#else
static long pathmax= 0;
#endif

static long posix_version= 0;
static long xsi_version= 0;

static volatile sig_atomic_t lock_sigflag;
static sigset_t lock_newset, lock_oldset, lock_zeroset;

#define PATH_MAX_GUESS 1024

char *path_alloc(size_t *sizep) {   /* also return allocated size, if nonnull */
    char *ptr;  
    size_t size;

    if (posix_version == 0)
        posix_version= sysconf(_SC_VERSION);

    if (xsi_version == 0)
        xsi_version= sysconf(_SC_XOPEN_VERSION);

    if (pathmax == 0) {
        errno= 0; 
        if ((pathmax = pathconf("/", _PC_PATH_MAX)) < 0) {
            if (errno == 0) 
                pathmax= PATH_MAX_GUESS;
            else
                err_sys("pathconf error for _PC_PATH_MAX");
        } else {
            pathmax++;
        } 
    }
    if ((posix_version < 200112L) && (xsi_version < 4)) 
        size= pathmax+1;
    else 
        size= pathmax;

    if ((ptr= malloc(size)) == NULL)
        err_sys("malloc error for pathname");
    
    if (sizep != NULL)
        *sizep= size; 

    return ptr;
}


void pr_exit(int status) {
    if (WIFEXITED(status))
        printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
    else if (WIFSIGNALED(status))
        printf("abnormal termination, signal number = %d%s\n", 
                WTERMSIG(status),
#ifdef WCOREDUMP
                WCOREDUMP(status) ? " (core file generated)" : "");
#else
                "");
#endif

    else if (WIFSTOPPED(status)) 
        printf("child stopped, signal number = %d\n", WSTOPSIG(status));

}

void pr_mask(const char *str) {
    sigset_t sigset;
    int errno_save;
    errno_save= errno;      /* we can be called by signal handlers */
    if (sigprocmask(0, NULL, &sigset) < 0) {
        err_ret("sigprocmask error");
    } else {
        printf("%s", str);
        if (sigismember(&sigset, SIGINT))
            printf(" SIGINT");
        if (sigismember(&sigset, SIGQUIT))
            printf(" SIGQUIT");
        if (sigismember(&sigset, SIGUSR1))
            printf(" SIGUSR1");
        if (sigismember(&sigset, SIGALRM))
            printf(" SIGALRM");
    }
    printf("\n");

    errno= errno_save;
}

Sigfunc * signal(int signo, Sigfunc *func)
{
    struct sigaction act, oact;
    act.sa_handler= func;
    sigemptyset(&act.sa_mask);
    act.sa_flags= 0;
    if (signo == SIGALRM) {
#ifdef SA_INTERRUPT
        act.sa_flags |= SA_INTERRUPT;
#endif
    } else {
        act.sa_flags |= SA_RESTART;
    }
    if (sigaction(signo, &act, &oact) < 0)
        return SIG_ERR;
    return oact.sa_handler;

}

Sigfunc * signal_intr(int signo, Sigfunc *func) {
    struct sigaction act, oact; 
    act.sa_handler= func;
    sigemptyset(&act.sa_mask);
    act.sa_flags= 0;
#ifdef SA_INTERRUPT    
        act.sa_flags |= SA_INTERRUPT;
#endif
    if (sigaction(signo, &act, &oact) < 0)
        return(SIG_ERR);
    return (oact.sa_handler);
}

void set_cloexec(int fd) {
    int val;
    if ((val= fcntl(fd, F_GETFL, 0)) < 0)
        err_sys("fcntl F_GETFL error");
    val |= FD_CLOEXEC;
    if (fcntl(fd, F_SETFL, val) < 0)
        err_sys("fcntl F_SETFL error");
}

void set_fl(int fd, int flags) {
    int val;
    if ((val = fcntl(fd, F_GETFL, 0)) < 0)
        err_sys("fcntl F_GETFL error");

    val |= flags;   /* turn on flags */
    if (fcntl(fd, F_SETFL, val) < 0)
        err_sys("fcntl F_SETFL error");
}

void clr_fl(int fd, int flags) {
    int val;
    if ((val = fcntl(fd, F_GETFL, 0)) < 0)
        err_sys("fcntl F_GETFL error");

    val &= ~flags;   /* turn on flags */
    if (fcntl(fd, F_SETFL, val) < 0)
        err_sys("fcntl F_SETFL error");
}

int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) {
    struct flock lock;
    lock.l_type= type;      /* F_RDLCK, FWRLCK, F_UNLCK */
    lock.l_start= offset;   /* byte offset, relative to l_whence */
    lock.l_whence= whence;  /* SEEK_SET, SEEK_CUR, SEEK_END */
    lock.l_len= len;        /* #bytes (0 means to EOF) */
    return(fcntl(fd, cmd, &lock));
}

int lock_test(int fd, int type, off_t offset, int whence, off_t len) {
    struct flock lock;
    lock.l_type= type;      /* F_RDLCK or F_WRLCK */
    lock.l_start= offset;   /* byte offset, relative to l_where */
    lock.l_whence= whence;  /* SEEK_SET, SEEK_CUR, SEEK_END */
    lock.l_len= len;        /* #bytes (0 means to EOF) */
    if (fcntl(fd, F_GETLK, &lock) < 0)
        err_sys("flock error");
    if (lock.l_type == F_UNLCK)
        return(0);          /* false, region isn't locked by another proc */
    return lock.l_pid;      /* true, return pid of lock owner */
}

static void lock_sigusr(int signo) {
    lock_sigflag= 1;
}

void TELL_WAIT() {
    struct sigaction signal;
    signal.sa_handler= lock_sigusr;
    sigemptyset(&signal.sa_mask);
    signal.sa_flags= 0;
    if (sigaction(SIGUSR1, &signal, NULL) < 0)
        err_sys("signal(SIG_USR1) error");
    if (sigaction(SIGUSR2, &signal, NULL) < 0)
        err_sys("signal(SIG_USR2) error");

    sigemptyset(&lock_zeroset);
    sigemptyset(&lock_newset);
    sigaddset(&lock_newset, SIGUSR1);
    sigaddset(&lock_newset, SIGUSR2);

    if (sigprocmask(SIG_BLOCK, &lock_newset, &lock_oldset) < 0)
        err_sys("SIG_BLOCK error");
}

void TELL_PARENT(pid_t pid) {
    kill(pid, SIGUSR1);
}

void TELL_CHILD(pid_t pid) {
    kill(pid, SIGUSR2);
}

void WAIT_PARENT() {
   while (lock_sigflag != 1) 
       sigsuspend(&lock_zeroset);
   lock_sigflag= 0;

   if (sigprocmask(SIG_SETMASK,&lock_oldset, NULL) < 0)
       err_sys("SIG_SETMASK error");
}

void WAIT_CHILD() {
    while (lock_sigflag != 1)
        sigsuspend(&lock_zeroset);
    lock_sigflag= 0;
    
    if (sigprocmask(SIG_SETMASK, &lock_oldset, NULL) < 0)
        err_sys("SIG_SETMASK error");
}

ssize_t readn(int fd, void *buf, size_t n) {
    int nread;
    ssize_t nleft= n;
    while (nleft > 0) {
        if ((nread= read(fd, buf, nleft)) < 0) {
            if (nleft == n) /*  */
                return -1; 
            else
                break;  /* error, return amount read so far */
        } else if (nread == 0) {    // EOF
            break;
        }
        buf+= nread;
        nleft-= nread;
    }
    return(n-nleft);
}

ssize_t writen(int fd, const void *buf, size_t n) {
    ssize_t nwrite;    
    size_t nleft= n;
    while (nleft > 0) {
        if ((nwrite= write(fd, buf, nleft)) < 0) {
            if (nleft == n)
                return -1;
            else
                break; /*error, return amount written so far */
        } else if (nwrite == 0) {   /* maybe died circle */
            break;
        }
        buf+= nwrite;
        nleft-= nwrite;
    }
    return n-nleft;
}


#ifdef OPEN_MAX
static int openmax = OPEN_MAX;
#else
static int openmax = 0;
#endif

#define OPEN_MAX_GUESS 256

int open_max(void) {
    if (openmax == 0) {
        errno= 0;
        if ((openmax= sysconf(_SC_OPEN_MAX)) < 0) {
            if((errno == 0))
                openmax= OPEN_MAX_GUESS;
            else
                err_sys("sysconf error for _SC_OPEN_MAX");
        }
    }
    return openmax;
}
